﻿using System;
using System.Drawing;
using System.IO;
using System.Net;
using System.Xml;
using System.Windows.Forms;

namespace TestMap2
{
    class GpsMapImage
    {
        public Image image;
        public bool ok = false;
        public bool finished = false;
        public string error;
        private int zoom_;
        private int zoom
        {
            get
            {
                return zoom_;
            }
            set
            {
                zoom_ = zoom;
                resolution = 2.0 * Math.PI * 6378137.0 / 256.0 / Math.Pow(2, zoom);
            }
        }
        private double resolution;
        private double originShift = 2.0 * Math.PI * 6378137 / 2.0;
        private Point upperLeftPixels;
        private int sizeY;
        private int sizeX;
        public string xmlRequest;
        public string imageRequest;
        GpsPoint centerPoint;

        public GpsMapImage(GpsPoint source, GpsPoint destination, int sizeX, int sizeY)
        {
            try
            {
                this.sizeY = sizeY;
                this.sizeX = sizeX;

                xmlRequest = "http://maps.googleapis.com/maps/api/directions/xml?origin=" + source.getCoordinates() + "&destination=" + destination.getCoordinates() + "&mode=walking&sensor=false";
                XmlDocument xDoc = new XmlDocument();
                xDoc.Load(XmlReader.Create(new StringReader((new StreamReader(((HttpWebResponse)(WebRequest.Create(xmlRequest)).GetResponse()).GetResponseStream())).ReadToEnd())));
                if (xDoc.GetElementsByTagName("status")[0].InnerText.Equals("OK"))
                {
                    GpsPoint upperLeft = new GpsPoint(Double.Parse(xDoc.GetElementsByTagName("northeast")[0].FirstChild.InnerText), Double.Parse(xDoc.GetElementsByTagName("southwest")[0].LastChild.InnerText));
                    GpsPoint bottomRight = new GpsPoint(Double.Parse(xDoc.GetElementsByTagName("southwest")[0].FirstChild.InnerText), Double.Parse(xDoc.GetElementsByTagName("northeast")[0].LastChild.InnerText));

                    zoom = (int)(Math.Log(Math.Min(((double)sizeX) / (bottomRight.X - upperLeft.X), ((double)sizeY) / (upperLeft.Y - bottomRight.Y)) * 1.46025) / Math.Log(2)) - 1;

                    centerPoint = upperLeft.getCenterGpsPoint(bottomRight);
                    imageRequest = "http://maps.google.com/maps/api/staticmap?path=color:0x0000FF80|weight:5|enc:" + xDoc.GetElementsByTagName("overview_polyline")[0].InnerText + "&markers=color:blue%7Clabel:Z%7C" + destination.getCoordinates() + "&size=" + sizeX + "x" + sizeY + "&sensor=false&center=" + centerPoint.getCoordinates() + "&zoom=";
                    image = new System.Drawing.Bitmap(((HttpWebResponse)(WebRequest.Create(imageRequest + zoom)).GetResponse()).GetResponseStream());

                    Point posun = degreesToPixels(centerPoint);
                    upperLeftPixels = new Point((int)Math.Round(posun.X - (double)sizeX / 2.0), (int)Math.Round(posun.Y - (double)sizeY / 2.0));
                    ok = true;
                }
            }
            catch (Exception ex)
            {
                error = ex.Message;
            }
            finished = true;
        }

        private void getImage()
        {
            finished = false;
            ok = false;
            try
            {
                image = new System.Drawing.Bitmap(((HttpWebResponse)(WebRequest.Create(imageRequest + zoom)).GetResponse()).GetResponseStream());

                Point posun = degreesToPixels(centerPoint);
                upperLeftPixels = new Point((int)Math.Round(posun.X - (double)sizeX / 2.0), (int)Math.Round(posun.Y - (double)sizeY / 2.0));
                ok = true;
            }

            catch (Exception ex)
            {
                error = ex.Message;
            }
            finished = true;
        }

        public void changeZoom(bool larger)
        {
            zoom += larger ? 1 : -1;
            getImage();
        }

        public void changeCenter(Point change)
        {
            Point tmpPoint = degreesToPixels(centerPoint);
            tmpPoint.Y += change.Y;
            tmpPoint.X += change.X;
            centerPoint = pixelsToDegrees(tmpPoint);
            getImage();
        }

        public GpsPoint pixelsToDegrees(Point pixels)
        {
            return new GpsPoint( 180.0 / Math.PI * (2.0 * Math.Atan(Math.Exp(((((double)pixels.Y * resolution - originShift) / originShift) * 180.0) * Math.PI / 180.0)) - Math.PI / 2.0), (((double)pixels.X * resolution - originShift) / originShift) * 180.0);
        }

        private Point degreesToPixels(GpsPoint gpsPoint)
        {
            return new Point((int)Math.Round(((gpsPoint.X * originShift / 180.0) + originShift) / resolution), (int)Math.Round((((Math.Log(Math.Tan((90.0 + gpsPoint.Y) * Math.PI / 360.0)) / (Math.PI / 180.0)) * originShift / 180.0) + originShift) / resolution));
        }

        public Point degreesToPixelsFromUpperLeft(GpsPoint gpsPoint)
        {
            Point point = degreesToPixels(gpsPoint);
            return new Point(point.X - upperLeftPixels.X, sizeY - (point.Y - upperLeftPixels.Y));
        }
    }
}